CMAKE_MINIMUM_REQUIRED(VERSION 3.1)

# Set up project properties
SET(PROJECT_NAMESPACE loco-3d)
SET(PROJECT_NAME crocoddyl)
SET(PROJECT_DESCRIPTION "Contact RObot COntrol by Differential DYnamic programming Library (Crocoddyl)")
SET(PROJECT_URL https://github.com/${PROJECT_NAMESPACE}/${PROJECT_NAME})

# Check if the submodule cmake have been initialized
IF(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake/base.cmake")
  MESSAGE(FATAL_ERROR "\nPlease run the following command first:\ngit submodule update --init\n")
ENDIF()

# Include important cmake modules
INCLUDE(cmake/base.cmake)
INCLUDE(cmake/test.cmake)
INCLUDE(cmake/boost.cmake)
INCLUDE(cmake/python.cmake)
INCLUDE(cmake/apple.cmake)

# Print initial message
MESSAGE(STATUS "${PROJECT_DESCRIPTION}, version ${PROJECT_VERSION}")
MESSAGE(STATUS "Copyright (C) 2018-2020 CNRS-LAAS")
MESSAGE(STATUS "Copyright (C) 2019-2020 University of Edinburgh")
MESSAGE(STATUS "All rights reserved.")
MESSAGE(STATUS "Released under the BSD 3-Clause License.")

# Set a default build type to 'Release' if none was specified
IF(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  MESSAGE(STATUS "Setting build type to 'Release' as none was specified.")
  SET(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
  # Set the possible values of build type for cmake-gui
  SET_PROPERTY(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
ENDIF()

SET(DOXYGEN_USE_MATHJAX YES)
SET(DOXYGEN_USE_TEMPLATE_CSS YES)

# Disable -Werror on Unix for now.
SET(CXX_DISABLE_WERROR True)
SET(PROJECT_USE_CMAKE_EXPORT True)

# Create different building options
OPTION(ENABLE_VECTORIZATION "Enable vectorization and further processor-related optimizations" OFF)
OPTION(BUILD_PYTHON_INTERFACE "Build the python binding" ON)
OPTION(BUILD_UNIT_TESTS "Build the unitary tests" ON)
OPTION(BUILD_BENCHMARK "Build the benchmark" ON)
OPTION(BUILD_EXAMPLES "Build the examples" ON)


IF(ENABLE_VECTORIZATION)
  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")
ENDIF()

COMPUTE_PROJECT_ARGS(PROJECT_ARGS LANGUAGES CXX)
PROJECT(${PROJECT_NAME} ${PROJECT_ARGS})

# If needed, fix CMake policy for APPLE systems
APPLY_DEFAULT_APPLE_CONFIGURATION()

# Add the different required and optional dependencies
ADD_PROJECT_DEPENDENCY(pinocchio 2.4.3 REQUIRED PKG_CONFIG_REQUIRES "pinocchio >= 2.4.3")
ADD_PROJECT_DEPENDENCY(example-robot-data 3.2.0 REQUIRED PKG_CONFIG_REQUIRES "example-robot-data >= 3.2.0")
ADD_OPTIONAL_DEPENDENCY("quadprog")
ADD_OPTIONAL_DEPENDENCY("scipy")

OPTION(BUILD_WITH_CODEGEN_SUPPORT "Build the library with the Code Generation support (required CppADCodeGen)" OFF)

OPTION(BUILD_WITH_MULTITHREADS "Build the library with the Multithreading support (required OpenMP)" OFF)
IF(BUILD_WITH_MULTITHREADS)
  SET(BUILD_WITH_NTHREADS "4" CACHE STRING "Number of threads")
  string(REGEX MATCH "^[0-9]+$" BUILD_WITH_NTHREADS  ${BUILD_WITH_NTHREADS})
  IF(NOT BUILD_WITH_NTHREADS MATCHES "^[0-9]+$")
    SET(BUILD_WITH_NTHREADS 4)
    MESSAGE(WARNING "the number of threads have to be an interger value, set to ${BUILD_WITH_NTHREADS}")
  ENDIF()
ENDIF()

IF(BUILD_WITH_CODEGEN_SUPPORT)
  ADD_REQUIRED_DEPENDENCY("cppad >= 20200000.0")
  ADD_REQUIRED_DEPENDENCY("cppadcg >= 2.4.1")
  #Pinocchio codegen related preproccessor defs.
  ADD_DEFINITIONS(-DPINOCCHIO_WITH_CPPADCG_SUPPORT)
  ADD_DEFINITIONS(-DPINOCCHIO_WITH_CPPAD_SUPPORT)
  SET(PACKAGE_EXTRA_MACROS "${PACKAGE_EXTRA_MACROS}\nADD_DEFINITIONS(-DPINOCCHIO_WITH_CPPAD_SUPPORT -DPINOCCHIO_WITH_CPPADCG_SUPPORT)")
  IF(NOT ${EIGEN3_VERSION} VERSION_GREATER "3.3.0")
    ADD_DEFINITIONS(-DPINOCCHIO_CPPAD_REQUIRES_MATRIX_BASE_PLUGIN)
    SET(PACKAGE_EXTRA_MACROS "${PACKAGE_EXTRA_MACROS}\nADD_DEFINITIONS(-DPINOCCHIO_CPPAD_REQUIRES_MATRIX_BASE_PLUGIN)")
  ENDIF(NOT ${EIGEN3_VERSION} VERSION_GREATER "3.3.0")  
  SET(CMAKE_CXX_STANDARD 11)
ENDIF()

# Add OpenMP
if(BUILD_WITH_MULTITHREADS)
  FIND_PACKAGE(OpenMP)
ENDIF()
if(OPENMP_FOUND AND BUILD_WITH_MULTITHREADS)
  SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp")
  ADD_DEFINITIONS(-DWITH_MULTITHREADING)
  ADD_DEFINITIONS(-DWITH_NTHREADS=${BUILD_WITH_NTHREADS})
  SET(PACKAGE_EXTRA_MACROS "${PACKAGE_EXTRA_MACROS}\nADD_DEFINITIONS(-DWITH_MULTITHREADING -DWITH_NTHREADS=${BUILD_WITH_NTHREADS})")
  SET(OMP_NUM_THREADS ${BUILD_WITH_NTHREADS})
ENDIF()

SET(BOOST_REQUIERED_COMPONENTS filesystem serialization system)
SET(BOOST_BUILD_COMPONENTS unit_test_framework)
SET(BOOST_OPTIONAL_COMPONENTS "")

IF(BUILD_PYTHON_INTERFACE)
  SET(BOOST_OPTIONAL_COMPONENTS ${BOOST_OPTIONAL_COMPONENTS} python)
  FINDPYTHON()
  ADD_PROJECT_DEPENDENCY(eigenpy 2.2.0 REQUIRED PKG_CONFIG_REQUIRES "eigenpy >= 2.2.0")
ENDIF(BUILD_PYTHON_INTERFACE)

SET(BOOST_COMPONENTS ${BOOST_REQUIERED_COMPONENTS} ${BOOST_OPTIONAL_COMPONENTS} ${BOOST_BUILD_COMPONENTS})
SEARCH_FOR_BOOST()

# Main library
FILE(GLOB_RECURSE ${PROJECT_NAME}_SOURCES
  src/*.cpp
  )

FILE(GLOB_RECURSE ${PROJECT_NAME}_HEADERS
  include/${PROJECT_NAME}/*.h*
  )

IF(UNIX)
  ADD_LIBRARY(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_SOURCES} ${${PROJECT_NAME}_HEADERS})
  SET_TARGET_PROPERTIES(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX)
  TARGET_INCLUDE_DIRECTORIES(${PROJECT_NAME} PUBLIC $<INSTALL_INTERFACE:include>)
  TARGET_LINK_LIBRARIES(${PROJECT_NAME} pinocchio::pinocchio)
  TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${Boost_FILESYSTEM_LIBRARY} ${Boost_SYSTEM_LIBRARY}
      ${Boost_SERIALIZATION_LIBRARY})

  if(OPENMP_FOUND)
    TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${OpenMP_CXX_LIBRARIES})
  ENDIF()

  INSTALL(TARGETS ${PROJECT_NAME} EXPORT ${TARGETS_EXPORT_NAME} DESTINATION lib)
ENDIF(UNIX)

# Build the Python interface
IF(BUILD_PYTHON_INTERFACE)
  # Include and add the bindings directory
  INCLUDE_DIRECTORIES(bindings)
  ADD_SUBDIRECTORY(bindings)
  ADD_SUBDIRECTORY(unittest/python/${PROJECT_NAME})
ENDIF(BUILD_PYTHON_INTERFACE)

# Build the unit tests
IF(BUILD_UNIT_TESTS)
  ADD_SUBDIRECTORY(unittest)
ENDIF(BUILD_UNIT_TESTS)

# Build the benchmark
IF(BUILD_BENCHMARK)
  ADD_SUBDIRECTORY(benchmark)
ENDIF(BUILD_BENCHMARK)

# Build the examples
IF(BUILD_EXAMPLES)
  ADD_SUBDIRECTORY(examples)
ENDIF(BUILD_EXAMPLES)

# PkgConfig packaging of the project
PKG_CONFIG_APPEND_LIBS(${PROJECT_NAME})
PKG_CONFIG_APPEND_BOOST_LIBS(${BOOST_REQUIRED_COMPONENTS})
